#ifndef _TIMERS_H_
#define _TIMERS_H_

#include "types.h"
#include "err.h"

#define TIMER_0_BASE 0x01C20000
#define TIMER_1_BASE 0x01C21000
#define TIMER_2_BASE 0x01F0C000
#define TIMER_3_BASE 0x1F0D000

#define USER_TIMER TIMER_0_BASE
#define WDOG_TIMER TIMER_1_BASE


#define MIN_TIMER_INTERVAL   10  //ms
#define MAX_TIMER_INTERVAL   21600000  // 6 hours!!


/*
* Timer 0 is clocked by PLL0 aux-clock
* which is the reference clock of external
* crystal. The external crystal is 24Mhz
*/
#define USER_TIMER_TICKS_IN_ONE_SEC   0x16E3600

#define NUM_TICKS_IN_ONE_MSEC         24000
#define NUM_TICKS_PER_BUCKET    NUM_TICKS_IN_ONE_MSEC // bucket size = 1ms
#define NUM_BUCKETS_IN_INTERVAL_MS(interval)   (interval)  // bucket size = 1ms

#define MIN_BUCKETS_INTERVAL  ((MIN_TIMER_INTERVAL)/(NUM_BUCKETS_IN_INTERVAL_MS(1)))


#define NUM_BITS_IN_WHEEL0  6
#define NUM_BITS_IN_WHEEL1  6
#define NUM_BITS_IN_WHEEL2  6

#define NUM_BUCKETS_IN_WHEEL0   (1 << NUM_BITS_IN_WHEEL0)
#define NUM_BUCKETS_IN_WHEEL1   (1 << NUM_BITS_IN_WHEEL1)
#define NUM_BUCKETS_IN_WHEEL2   (1 << NUM_BITS_IN_WHEEL2)
#define TOTAL_BUCKETS_IN_WHEELS (NUM_BUCKETS_IN_WHEEL0 + NUM_BUCKETS_IN_WHEEL1 + \
                     NUM_BUCKETS_IN_WHEEL2)
#define NUM_WHEELS_BITS         (NUM_BITS_IN_WHEEL0 + NUM_BITS_IN_WHEEL1 + \
                                 NUM_BITS_IN_WHEEL2)

/*
* CUR_Wx(N): Current bucket number in wheel x
*/
#define CUR_W0(x) (x & (NUM_BUCKETS_IN_WHEEL0 - 1))
#define CUR_W1(x) ((x >>NUM_BITS_IN_WHEEL0) & (NUM_BUCKETS_IN_WHEEL1 - 1))
#define CUR_W2(x) (((x >>NUM_BITS_IN_WHEEL0) >>NUM_BITS_IN_WHEEL1) & \
                (NUM_BUCKETS_IN_WHEEL2 - 1))


#define TIM_REV(timer)				*((volatile uint32_t *)(timer + 0x00))
#define TIM_EMUMGT(timer)			*((volatile uint32_t *)(timer + 0x04))
#define TIM_GPINTGPEN(timer)			*((volatile uint32_t *)(timer + 0x08))
#define TIM_GPDATGPDIR(timer)			*((volatile uint32_t *)(timer + 0x0c))
#define TIM_TIM12(timer)			*((volatile uint32_t *)(timer + 0x10))
#define TIM_TIM34(timer)			*((volatile uint32_t *)(timer + 0x14))
#define TIM_PRD12(timer)			*((volatile uint32_t *)(timer + 0x18))
#define TIM_PRD34(timer)			*((volatile uint32_t *)(timer + 0x1c))
#define TIM_TCR(timer)				*((volatile uint32_t *)(timer + 0x20))
#define TIM_TGCR(timer)				*((volatile uint32_t *)(timer + 0x24))
#define TIM_WDTCR(timer)			*((volatile uint32_t *)(timer + 0x28))
#define TIM_REL12(timer)			*((volatile uint32_t *)(timer + 0x34))
#define TIM_REL34(timer)			*((volatile uint32_t *)(timer + 0x38))
#define TIM_CAP12(timer)			*((volatile uint32_t *)(timer + 0x3c))
#define TIM_CAP34(timer)			*((volatile uint32_t *)(timer + 0x40))
#define TIM_INTCTLSTAT(timer)			*((volatile uint32_t *)(timer + 0x44))
#define TIM_CMP0(timer)				*((volatile uint32_t *)(timer + 0x60))
#define TIM_CMP1(timer)				*((volatile uint32_t *)(timer + 0x64))
#define TIM_CMP2(timer)				*((volatile uint32_t *)(timer + 0x68))
#define TIM_CMP3(timer)				*((volatile uint32_t *)(timer + 0x6c))
#define TIM_CMP4(timer)				*((volatile uint32_t *)(timer + 0x70))
#define TIM_CMP5(timer)				*((volatile uint32_t *)(timer + 0x74))
#define TIM_CMP6(timer)				*((volatile uint32_t *)(timer + 0x78))
#define TIM_CMP7(timer)				*((volatile uint32_t *)(timer + 0x7c))



/*
* Timer format
*/
typedef struct tim_entry_t
{
    struct tim_entry_t *next;
    struct tim_entry_t *prev;
    struct tim_entry_t *sentinel;

    //uint32_t init_buckets;
    uint32_t buckets_remaining;  //In case interval exceeds max wheels

    /*
    * Timer data passed by the caller
    * Transparent to timer module
    * Should not be referenced inside timer module
    * Could lead to memory exception
    */
    void *caller_ctx;
}tim_entry_t;

/*
* Timer bucket
*/
typedef struct
{
    tim_entry_t sentinel;
}bucket_t;

/*
* Timer wheel
*/
typedef struct 
{
  uint8_t mask;
  uint8_t shift;
}wheel_info_t;


/*
* Initialize timer module
* IN: OS handler to be called when
*     a submitted timer expires
* OUT: Size of the timer-struct (tim_entry_t)
*     to be allocated and passed while
*     submitting timers
*/
err_t timer_init(void (*expiry_handler)(void *, void *), uint32_t *tim_entry_size);
err_t timer_submit(void *handle, void *tim_data, uint32_t interval_ms);
err_t cancel_submitted_timer(void *handle);
void reset_processor(uint32_t data);

#endif
